/*!
 * \file WTSExpressData.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 指标表达式数据定义文件
 */
#pragma once
#include <stdint.h>
#include "WTSDataDef.hpp"
#include "WTSMarcos.h"

#ifdef _MSC_VER
#include <WTypes.h>
#else
typedef unsigned long	COLORREF;
typedef unsigned char	BYTE;
typedef unsigned short	WORD;
typedef unsigned long	DWORD;
#define RGB(r,g,b)	((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
#endif

NS_WTP_BEGIN

//////////////////////////////////////////////////////////////////////////
/*!
 * \brief 线段信息类，用于指标图形显示的线段样式设置
 * 
 * \details 该类封装了指标线段的显示属性，包括：
 *          - 线段颜色设置（RGB颜色值）
 *          - 线段宽度控制（像素单位）
 *          - 线段样式定义（实线、虚线等）
 *          主要用于指标图表的可视化渲染和用户界面显示
 * 
 * \author Wesley
 * \date 2020/03/30
 */
class WTSLineInfo : public WTSObject
{
public:
	/*!
	 * \brief 创建线段信息对象的静态工厂方法
	 * 
	 * \details 根据指定的颜色、宽度和样式创建线段信息对象，
	 *          用于指标图形的线段渲染配置
	 * 
	 * \param clr 线段颜色，使用COLORREF类型的RGB值
	 * \param width 线段宽度，单位为像素，默认为1像素
	 * \param style 线段样式，0为实线，其他值定义不同样式，默认为0
	 * 
	 * \return 创建成功的线段信息对象指针
	 * 
	 * \note 返回的对象需要调用者负责内存管理
	 */
	static WTSLineInfo* create(COLORREF clr, int width = 1, int style = 0)
	{
		WTSLineInfo* pRet = new WTSLineInfo();
		pRet->_line_color = clr;
		pRet->_line_width = width;
		pRet->_line_style = style;
		return pRet;
	}

	/*!
	 * \brief 获取线段颜色
	 * 
	 * \return COLORREF类型的RGB颜色值
	 */
	COLORREF color() const{return _line_color;}
	
	/*!
	 * \brief 获取线段宽度
	 * 
	 * \return 线段宽度值，单位为像素
	 */
	int width() const{return _line_width;}
	
	/*!
	 * \brief 获取线段样式
	 * 
	 * \return 线段样式值，0表示实线，其他值表示不同的线段样式
	 */
	int style() const{return _line_style;}

protected:
	/*!
	 * \brief 默认构造函数
	 * 
	 * \details 初始化线段为白色、1像素宽度的实线
	 */
	WTSLineInfo()
	:_line_color(RGB(255,255,255))
	,_line_width(1)
	,_line_style(0){}

protected:
	COLORREF	_line_color;	//线段颜色
	int			_line_width;	//线段宽度
	int			_line_style;	//线段样式
};


//////////////////////////////////////////////////////////////////////////
/*!
 * \brief 指标参数类，用于存储和管理指标计算所需的参数
 * 
 * \details 该类提供动态参数管理功能，包括：
 *          - 动态添加指标计算参数
 *          - 参数的设置和获取操作
 *          - 参数数量的查询和索引访问
 *          主要用于技术指标计算时的参数传递和配置
 * 
 * \author Wesley
 * \date 2020/03/30
 */
class WTSExpressParams : public WTSObject
{
public:
	/*!
	 * \brief 创建指标参数对象的静态工厂方法
	 * 
	 * \details 创建一个空的参数容器，可以动态添加指标计算所需的参数
	 * 
	 * \return 创建成功的指标参数对象指针
	 * 
	 * \note 返回的对象需要调用者负责内存管理
	 */
	static	WTSExpressParams* create()
	{
		WTSExpressParams* pRet = new WTSExpressParams;

		return pRet;
	}

	/*!
	 * \brief 添加一个参数到参数列表末尾
	 * 
	 * \details 向参数容器中追加一个新的整数参数，用于指标计算
	 * 
	 * \param param 要添加的整数参数值
	 */
	void	addParam(int param)
	{
		m_vecParams.emplace_back(param);
	}

	/*!
	 * \brief 设置指定索引位置的参数值
	 * 
	 * \details 修改参数列表中指定位置的参数值，如果索引超出范围则忽略操作
	 * 
	 * \param idx 参数索引，从0开始
	 * \param param 新的参数值
	 */
	void	setParam(uint32_t idx, int param)
	{
		if(idx >= m_vecParams.size())
			return;

		m_vecParams[idx] = param;
	}

	/*!
	 * \brief 获取指定索引位置的参数值
	 * 
	 * \details 返回参数列表中指定位置的参数值，如果索引超出范围返回无效值
	 * 
	 * \param idx 参数索引，从0开始
	 * 
	 * \return 参数值，如果索引无效返回INVALID_INT32
	 */
	int		getParam(uint32_t idx) const
	{
		if(idx >= m_vecParams.size())
			return INVALID_INT32;

		return m_vecParams[idx];
	}

	/*!
	 * \brief 获取参数的数量
	 * 
	 * \return 当前存储的参数总数
	 */
	uint32_t	getParamCount() const{return m_vecParams.size();}

	/*!
	 * \brief 重载下标操作符，提供数组式的参数访问
	 * 
	 * \param idx 参数索引
	 * 
	 * \return 参数值的引用，可用于读取和修改
	 */
	int&	operator[](uint32_t idx){return m_vecParams[idx];}

protected:
	vector<int>		m_vecParams;	//参数列表
};

//////////////////////////////////////////////////////////////////////////
/*!
 * \brief 指标线段类，继承自数值数组，用于存储指标计算结果
 * 
 * \details 该类封装了指标计算的结果数据和显示属性，包括：
 *          - 数值序列的存储（继承自WTSValueArray）
 *          - 线段类型和显示样式的配置
 *          - 小数位数和格式化设置
 *          - 线段标签和显示信息管理
 *          主要用于指标计算结果的存储和图形化显示
 * 
 * \author Wesley
 * \date 2020/03/30
 */
class WTSExpressLine : public WTSValueArray
{
public:
	/*!
	 * \brief 创建指标线段对象的静态工厂方法
	 * 
	 * \details 根据指定参数创建指标线段对象，并分配存储空间
	 * 
	 * \param size 数据点数量，用于分配存储空间
	 * \param lineType 线段类型，默认为WELT_Polyline（折线）
	 * \param decimal 小数位数，用于数值格式化显示，默认为0
	 * \param uStyle 显示样式标志，默认为线段和标题都可见
	 * 
	 * \return 创建成功的指标线段对象指针
	 * 
	 * \note 返回的对象需要调用者负责内存管理
	 */
	static WTSExpressLine*	create(uint32_t size, WTSExpressLineType lineType = WELT_Polyline, uint32_t decimal = 0, uint32_t uStyle = ELS_LINE_VISIBLE|ELS_TITLE_VISIBLE)
	{
		WTSExpressLine* pRet = new WTSExpressLine;
		pRet->m_uDecimal = decimal;
		pRet->m_uStyle = uStyle;
		pRet->m_lineType = lineType;
		pRet->resize(size);

		return pRet;
	}

	/*!
	 * \brief 默认构造函数
	 * 
	 * \details 初始化线段信息数组为NULL
	 */
	WTSExpressLine(): m_ayLineInfo(NULL){}

	/*!
	 * \brief 释放对象资源
	 * 
	 * \details 重写基类的释放方法，确保线段信息数组也被正确释放
	 */
	virtual void release()
	{
		if(isSingleRefs() && m_ayLineInfo)
		{
			m_ayLineInfo->release();
		}

		WTSObject::release();
	}

	/*!
	 * \brief 增加对象引用计数
	 * 
	 * \details 重写基类的引用方法，确保引用计数正确管理
	 */
	virtual void retain()
	{
		WTSObject::retain();
	}

	/*!
	 * \brief 获取数值格式化字符串
	 * 
	 * \details 根据设置的小数位数生成printf风格的格式化字符串，
	 *          用于数值的统一格式化显示
	 * 
	 * \return 格式化字符串，如"%.2f"表示保留2位小数
	 */
	const char* getFormat()
	{
		if(m_strFormat.empty())
		{
			char format[12] = {0};
			sprintf(format, "%%.%df", m_uDecimal);
			m_strFormat = format;
		}

		return m_strFormat.c_str();
	}

	/*!
	 * \brief 获取数据的小数位数
	 * 
	 * \return 小数位数值
	 */
	uint32_t	getDecimal(){return m_uDecimal;}

	/*!
	 * \brief 添加线段显示信息
	 * 
	 * \details 为该指标线段添加显示样式信息，支持多种线段样式
	 * 
	 * \param lineInfo 线段信息对象，包含颜色、宽度、样式等
	 */
	void		addLineInfo(WTSLineInfo* lineInfo)
	{
		if(m_ayLineInfo == NULL)
		{
			m_ayLineInfo = WTSArray::create();
		}
		m_ayLineInfo->append(lineInfo, true);
	}

	/*!
	 * \brief 获取指定索引的线段显示信息
	 * 
	 * \details 返回指定索引位置的线段显示信息，用于图形渲染
	 * 
	 * \param idx 线段信息索引，默认为0
	 * 
	 * \return 线段信息对象指针，如果索引无效则返回NULL
	 */
	WTSLineInfo*	getLineInfo(uint32_t idx = 0)
	{
		if(m_ayLineInfo == NULL || m_ayLineInfo->size()==0 || idx >= m_ayLineInfo->size())
			return NULL;

		return STATIC_CONVERT(m_ayLineInfo->at(idx), WTSLineInfo*);
	}

	/*!
	 * \brief 清除所有线段显示信息
	 * 
	 * \details 清空线段信息列表，重置显示样式
	 */
	void		clearLineInfo()
	{
		if(m_ayLineInfo)
			m_ayLineInfo->clear();
	}

	/*!
	 * \brief 设置线段标签
	 * 
	 * \details 为线段设置文本标签，用于图表中的图例显示
	 * 
	 * \param tag 标签文本
	 */
	void		setLineTag(const char* tag){m_strLineTag = tag;}
	
	/*!
	 * \brief 获取线段标签
	 * 
	 * \return 线段标签文本
	 */
	const char*	getLineTag(){return m_strLineTag.c_str();}

	/*!
	 * \brief 检查是否具有指定的显示样式
	 * 
	 * \details 判断当前线段是否包含指定的样式标志
	 * 
	 * \param uStyle 要检查的样式标志
	 * 
	 * \return true表示具有该样式，false表示不具有
	 */
	bool		isStyle(uint32_t uStyle) const{ return (m_uStyle & uStyle) == uStyle; }

	/*!
	 * \brief 设置线段类型
	 * 
	 * \details 设置线段的显示类型，如折线、柱状图等
	 * 
	 * \param lineType 线段类型枚举值
	 */
	void		setLineType(WTSExpressLineType lineType){m_lineType = lineType;}
	
	/*!
	 * \brief 获取线段类型
	 * 
	 * \return 当前线段的类型枚举值
	 */
	WTSExpressLineType getLineType() const{return m_lineType;}

protected:
	WTSArray*		m_ayLineInfo;	//线段信息数组
	std::string		m_strLineTag;	//线段标签
	uint32_t		m_uStyle;		//显示样式标志

	uint32_t		m_uDecimal;		//小数位数
	std::string		m_strFormat;	//格式化字符串
	WTSExpressLineType	m_lineType;	//线段类型
};
typedef vector<WTSExpressLine*>	WTSVecExpLines;


//////////////////////////////////////////////////////////////////////////
/*!
 * \brief 指标数据类，用于管理完整的指标计算结果和配置信息
 * 
 * \details 该类是指标系统的核心容器，提供以下功能：
 *          - 多条指标线段的统一管理
 *          - 指标类型和标题的配置
 *          - 基准线和分割比例的设置
 *          - 线段间的交叉检测算法
 *          - 指标数值的统计分析功能
 *          主要用于技术分析指标的完整数据表示和运算
 * 
 * \author Wesley
 * \date 2020/03/30
 */
class WTSExpressData : public WTSObject
{
public:
	/*!
	 * \brief 创建指标数据对象的静态工厂方法
	 * 
	 * \details 根据指定的标题和类型创建指标数据对象
	 * 
	 * \param title 指标标题，用于显示和识别
	 * \param eType 指标类型，默认为WET_Unique（唯一类型）
	 * 
	 * \return 创建成功的指标数据对象指针
	 * 
	 * \note 返回的对象需要调用者负责内存管理
	 */
	static WTSExpressData*	create(const char* title, WTSExpressType eType = WET_Unique)
	{
		WTSExpressData* pRet = new WTSExpressData;
		pRet->setTitle(title);
		pRet->setType(eType);
		return pRet;
	}

protected:
	/*!
	 * \brief 默认构造函数
	 * 
	 * \details 初始化所有成员变量为默认值
	 */
	WTSExpressData() :m_ayExpLines(NULL), m_dDevide(1.0), m_dBaseLine(0.0), m_bBaseLine(false){}

	/*!
	 * \brief 虚析构函数
	 * 
	 * \details 确保派生类能够正确析构
	 */
	virtual ~WTSExpressData()
	{
		
	}

public:
	/*!
	 * \brief 释放对象资源
	 * 
	 * \details 重写基类的释放方法，确保指标线段数组也被正确释放
	 */
	virtual void release()
	{
		if (isSingleRefs() && m_ayExpLines)
			m_ayExpLines->release();

		WTSObject::release();
	}

	/*!
	 * \brief 设置指标标题
	 * 
	 * \param title 指标标题文本
	 */
	void		setTitle(const char* title){m_strExpTitle = title;}
	
	/*!
	 * \brief 获取指标标题
	 * 
	 * \return 指标标题文本
	 */
	const char* getTitle() const{return m_strExpTitle.c_str();}

	/*!
	 * \brief 设置指标类型
	 * 
	 * \param eType 指标类型枚举值
	 */
	void		setType(WTSExpressType eType){m_expType = eType;}
	
	/*!
	 * \brief 获取指标类型
	 * 
	 * \return 指标类型枚举值
	 */
	WTSExpressType	getType() const{return m_expType;}

	/*!
	 * \brief 设置基准线配置
	 * 
	 * \details 配置指标的基准线显示，用于参考水平线的绘制
	 * 
	 * \param bEnable 是否启用基准线，默认为true
	 * \param dBaseLine 基准线的数值位置，默认为0.0
	 */
	void		setBaseLine(bool bEnable = true, double dBaseLine = 0.0)
	{
		m_bBaseLine = bEnable;
		m_dBaseLine = dBaseLine;
	}

	/*!
	 * \brief 检查是否启用了基准线
	 * 
	 * \return true表示启用基准线，false表示未启用
	 */
	bool		hasBaseLine() const{ return m_bBaseLine; }
	
	/*!
	 * \brief 获取基准线数值
	 * 
	 * \return 基准线的Y轴坐标值
	 */
	double		getBaseLine() const{ return m_dBaseLine; }

	/*!
	 * \brief 添加指标线段到数据集
	 * 
	 * \details 将一条指标线段添加到当前指标数据中，支持多线段指标
	 * 
	 * \param line 要添加的指标线段对象
	 * 
	 * \return 线段在数据集中的索引，如果添加失败返回INVALID_UINT32
	 */
	uint32_t	addExpLine(WTSExpressLine* line)
	{
		if(NULL == line)
			return INVALID_UINT32;

		if(NULL == m_ayExpLines)
			m_ayExpLines = WTSArray::create();

		m_ayExpLines->append(line, false);

		return m_ayExpLines->size() - 1;
	}

	/*!
	 * \brief 获取指定索引的指标线段
	 * 
	 * \details 返回指标数据中指定位置的线段对象
	 * 
	 * \param idx 线段索引，从0开始
	 * 
	 * \return 指标线段对象指针，如果索引无效返回NULL
	 */
	WTSExpressLine*	getExpLine(uint32_t idx)
	{
		if(NULL == m_ayExpLines || idx >= m_ayExpLines->size())
			return NULL;

		return STATIC_CONVERT(m_ayExpLines->at(idx), WTSExpressLine*);
	}

	/*!
	 * \brief 获取指标线段的数量
	 * 
	 * \return 当前指标数据包含的线段总数
	 */
	uint32_t	getLineCount(){return (NULL == m_ayExpLines)?0:m_ayExpLines->size();}

	/*!
	 * \brief 检测两条线段是否发生向上交叉
	 * 
	 * \details 判断线段idx0是否从下方向上穿越线段idx1，
	 *          常用于金叉等技术分析信号的检测
	 * 
	 * \param idx0 第一条线段的索引
	 * \param idx1 第二条线段的索引
	 * 
	 * \return true表示发生向上交叉，false表示未发生
	 * 
	 * \note 需要至少2个数据点才能进行交叉检测
	 */
	bool		crossOver(uint32_t idx0, uint32_t idx1)
	{
		if(NULL == m_ayExpLines)
			return false;

		if(idx0 >= m_ayExpLines->size() || idx1 >= m_ayExpLines->size())
			return false;

		WTSExpressLine* line0 = STATIC_CONVERT(m_ayExpLines->at(idx0),WTSExpressLine*);
		WTSExpressLine* line1 = STATIC_CONVERT(m_ayExpLines->at(idx1),WTSExpressLine*);

		if(line0->size() < 2 || line1->size() < 2)
			return false;

		double preValue0 = line0->at(line0->size()-2);
		double curValue0 = line0->at(line0->size()-1);

		double preValue1 = line1->at(line1->size()-2);
		double curValue1 = line1->at(line1->size()-1);

		if(preValue0 <= preValue1 && curValue0 > curValue1)
			return true;

		return false;
	}

	/*!
	 * \brief 检测两条线段是否发生向下交叉
	 * 
	 * \details 判断线段idx0是否从上方向下穿越线段idx1，
	 *          常用于死叉等技术分析信号的检测
	 * 
	 * \param idx0 第一条线段的索引
	 * \param idx1 第二条线段的索引
	 * 
	 * \return true表示发生向下交叉，false表示未发生
	 * 
	 * \note 需要至少2个数据点才能进行交叉检测
	 */
	bool		crossUnder(uint32_t idx0, uint32_t idx1)
	{
		if(NULL == m_ayExpLines)
			return false;

		if(idx0 >= m_ayExpLines->size() || idx1 >= m_ayExpLines->size())
			return false;

		WTSExpressLine* line0 = STATIC_CONVERT(m_ayExpLines->at(idx0),WTSExpressLine*);
		WTSExpressLine* line1 = STATIC_CONVERT(m_ayExpLines->at(idx1),WTSExpressLine*);

		if(line0->size() < 2 || line1->size() < 2)
			return false;

		double preValue0 = line0->at(line0->size()-2);
		double curValue0 = line0->at(line0->size()-1);

		double preValue1 = line1->at(line1->size()-2);
		double curValue1 = line1->at(line1->size()-1);

		if(preValue0 >= preValue1 && curValue0 < curValue1)
			return true;

		return false;
	}

	/*!
	 * \brief 计算指定范围内所有线段的最大值
	 * 
	 * \details 遍历所有指标线段，找出指定数据范围内的最大数值，
	 *          用于图表的Y轴范围自动调整
	 * 
	 * \param head 起始位置索引（从后往前计算）
	 * \param tail 结束位置索引（从后往前计算）
	 * 
	 * \return 范围内的最大值，如果无有效数据返回INVALID_DOUBLE
	 */
	double		maxvalue(int32_t head, int32_t tail) const
	{
		double ret = INVALID_DOUBLE;
		WTSArray::ConstIterator it = m_ayExpLines->begin();
		for(; it != m_ayExpLines->end(); it++)
		{
			WTSExpressLine* line = STATIC_CONVERT(*it, WTSExpressLine*);
			bool bAbs = (line->getLineType() == WELT_VolStick || line->getLineType() == WELT_AStickLine);
			double v = line->maxvalue(head, tail, bAbs);
			if(v == INVALID_DOUBLE)
				continue;
			if(ret == INVALID_DOUBLE)
				ret = v;
			else
				ret = max(ret, v);
		}

		return ret;
	}

	/*!
	 * \brief 计算指定范围内所有线段的最小值
	 * 
	 * \details 遍历所有指标线段，找出指定数据范围内的最小数值，
	 *          用于图表的Y轴范围自动调整
	 * 
	 * \param head 起始位置索引（从后往前计算）
	 * \param tail 结束位置索引（从后往前计算）
	 * 
	 * \return 范围内的最小值，如果无有效数据返回INVALID_DOUBLE
	 * 
	 * \note 对于成交量等特殊线段类型，最小值固定为0
	 */
	double		minvalue(int32_t head, int32_t tail) const
	{
		double ret = INVALID_DOUBLE;
		WTSArray::ConstIterator it = m_ayExpLines->begin();
		for(; it != m_ayExpLines->end(); it++)
		{
			WTSExpressLine* line = STATIC_CONVERT(*it, WTSExpressLine*);
			bool bAbs = (line->getLineType() == WELT_VolStick || line->getLineType() == WELT_AStickLine);
			if(bAbs)//原来是成交量柱，最小从0开始计算的
				return 0;
			double v = line->minvalue(head, tail, bAbs);
			if (v == INVALID_DOUBLE)
				continue;
			if(ret == INVALID_DOUBLE)
				ret = v;
			else
				ret = min(ret, v);
		}

		return ret;
	}

	/*!
	 * \brief 获取指标数据的大小
	 * 
	 * \details 返回指标数据中数据点的数量，以第一条线段的大小为准
	 * 
	 * \return 数据点数量，如果没有线段返回0
	 */
	uint32_t	size() const
	{
		if(NULL == m_ayExpLines || m_ayExpLines->size()==0)
			return 0;

		return STATIC_CONVERT(m_ayExpLines->at(0), WTSExpressLine*)->size();
	}

	/*!
	 * \brief 获取数值精度
	 * 
	 * \return 当前设置的精度值
	 */
	uint32_t	getPrecision() const { return m_uPrec; }
	
	/*!
	 * \brief 设置数值精度
	 * 
	 * \param prec 精度值
	 */
	void setPrecision(uint32_t prec){ m_uPrec = prec; }

	/*!
	 * \brief 设置分割比例
	 * 
	 * \details 设置指标显示的分割比例，用于多指标窗口的空间分配
	 * 
	 * \param dvd 分割比例值
	 */
	void		setDevide(double dvd){m_dDevide = dvd;}
	
	/*!
	 * \brief 获取分割比例
	 * 
	 * \return 当前的分割比例值
	 */
	double		getDevide() const{return m_dDevide;}

protected:
	WTSArray*		m_ayExpLines;	//指标线段数组
	std::string		m_strExpTitle;	//指标标题
	WTSExpressType	m_expType;		//指标类型
	uint32_t		m_uPrec;		//数值精度
	double			m_dDevide;		//分割比例

	bool			m_bHasTitle;	//是否有标题

	bool			m_bBaseLine;	//是否启用基准线
	double			m_dBaseLine;	//基准线数值
};

NS_WTP_END